﻿#include "general.h"

#include <QDebug>
#include <QRegularExpression>
#include <QRegularExpressionMatch>

template qint64 caculation<qint64>(const QString str);
template double caculation<double>(const QString str);

bool isAllDigits(const QString& str) {
    for (QChar c : str) {
        if (!c.isDigit()) {
            return false;
        }
    }
    return true;
}

int getNextNumberFromString(const QString iFormulaStr, QString& res_str) {
    int i = 0;

    do {
        if (iFormulaStr[i] == ' ') continue;

        if ( (iFormulaStr[i] != '+') &&
            (iFormulaStr[i] != '-') &&
            (iFormulaStr[i] != '*') &&
            (iFormulaStr[i] != '/') &&
            (iFormulaStr[i] != '(') &&
            (iFormulaStr[i] != ')') ) {
            res_str += iFormulaStr[i];
        } else break;
    }while(++i < iFormulaStr.length() && iFormulaStr[i].toLatin1() != '\0');
    return i - 1;
}

int getFirstOperationSignPos(const QString iString) {
    int index = 0;

    if (iString.length() == 0) return index;
    bool flag = false;

    do {
        if (!flag && iString[index] == ' ') continue;
        else if (flag && iString[index] == ' ') break;

        if ( (iString[index] == '+') ||
            (iString[index] == '-') ||
            (iString[index] == '*') ||
            (iString[index] == '/') ||
            (iString[index] == '(') ||
            (iString[index] == ')') ) {
            break;
        } else {
            flag = true;
        }

    }while(++index < iString.length() && iString[index].toLatin1() != '\0');

    return index;
}


bool isOperationSign(QChar ch) {
    if ( ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '(' || ch == ')') return true;
    else return false;
}

template<typename T>
T caculateBySign(T num1, T num2, QChar sign) {
    switch(sign.toLatin1()) {
    case '+':
        return (num1 + num2);
        break;

    case '-':
        return (num1 - num2);
        break;

    case '*':
        return (num1 * num2);
        break;

    case '/':
        return (num1 / num2);
        break;

    default:
        return 0;
        break;
    }
}





template<typename T>
void printStack(QStack<T> const& iStack) {
    QStack<T> tmp = iStack;
    QString res ="[";
    while (!tmp.isEmpty()) {
        res += tmp.top();
        tmp.pop();
        if (!tmp.isEmpty()) res += ",";
    }
    res += "]";
    qDebug()<<res;
}



template<typename T>
T caculation(const QString str) {
    if (isAllDigits(str)) {
        if (std::is_integral<T>::value) {
            return str.toLongLong();
        } else if (std::is_floating_point<T>::value) {
            return str.toDouble();
        }
    }

    QStack<T> number_stack;
    QStack<QChar> operation_sign_stack;

    int pos = 0;
    do {
        if (str[pos] == ' ') {
            continue;
        }

        // 如果是左括号，则符号压栈
        if (str[pos] == '(') {
            operation_sign_stack.push(str[pos]);
            continue;
        }

        // 如果是右边括号，则弹出栈顶2个元素和运算符栈顶1个运算符进行计算，将计算结果重新压栈
        if (str[pos] == ')') {
            QStack<T> tmp_number_stack;
            QStack<QChar> tmp_operation_sign_stack;
            while(operation_sign_stack.top() != '(') {
                // 获取符号栈的栈顶计算符号
                QChar sign = operation_sign_stack.top();
                operation_sign_stack.pop();

                T num1 = number_stack.top();
                number_stack.pop();

                if (sign == '*' || sign == '/') {
                    // 如果计算符号是*或/，优先计算
                    // 把计算结果压入临时计算数据栈
                    T num2 = number_stack.top();
                    number_stack.pop();
                    if (std::is_integral<T>::value) {
                        tmp_number_stack.push(caculateBySign<qint64>(num1, num2, sign));
                    } else if (std::is_floating_point<T>::value) {
                        tmp_number_stack.push(caculateBySign<double>(num1, num2, sign));
                    }

                } else {
                    tmp_number_stack.push(num1);
                    // 如果计算符号不是*或/，压栈
                    tmp_operation_sign_stack.push(sign);
                }

            }

            while (tmp_operation_sign_stack.size() > 0) {
                T num1 = tmp_number_stack.top();
                tmp_number_stack.pop();
                T num2;
                if (tmp_number_stack.size() > 0) {
                    num2 = tmp_number_stack.top();
                    tmp_number_stack.pop();
                }
                else {
                    num2 = number_stack.top();
                    number_stack.pop();
                }

                QChar sign = tmp_operation_sign_stack.top();
                tmp_operation_sign_stack.pop();
                if (std::is_integral<T>::value) {
                    tmp_number_stack.push(caculateBySign<qint64>(num2, num1, sign));
                } else if (std::is_floating_point<T>::value) {
                    tmp_number_stack.push(caculateBySign<double>(num1, num2, sign));
                }
            }
            number_stack.push(tmp_number_stack.top());
            operation_sign_stack.pop();
            continue;
        }

        // 如果是运算符，则将运算符压入运算符数据栈
        if (isOperationSign(str[pos])) {
            operation_sign_stack.push(str[pos]);
            continue;
        }

        // 以上均不满足，则提取数字压入数字栈
        QString num;
        pos += getNextNumberFromString(str.mid(pos, str.length() - pos), num);
        if (std::is_integral<T>::value) {
            number_stack.push(num.toLongLong());
        } else if (std::is_floating_point<T>::value) {
            number_stack.push(num.toDouble());
        }

    }while(++pos < str.length() && str[pos].toLatin1() != '\0');

    while (operation_sign_stack.length() > 0) {
        T num1 = number_stack.top();
        number_stack.pop();
        T num2 = number_stack.top();
        number_stack.pop();
        QChar sign = operation_sign_stack.top();
        operation_sign_stack.pop();
        if (std::is_integral<T>::value) {
            number_stack.push(caculateBySign<qint64>(num1, num2, sign));
        } else if (std::is_floating_point<T>::value) {
            number_stack.push(caculateBySign<double>(num1, num2, sign));
        }

    }

    return number_stack.top();

}


qint64 caculationByRule(QString rule, QList<QPair<QString, QString>> list) {
    QString tmpRule;
    for (auto const &item : std::as_const(list)) {
        tmpRule = rule.replace(item.first, item.second);
    }
    return caculation<int>(tmpRule);
}





QString removeOperation(QString str) {
    if (str.contains("=")) str = str.replace("=", "");
    if (str.contains(">")) str = str.replace(">", "");
    if (str.contains("<")) str = str.replace("<", "");
    if (str.contains(">=")) str = str.replace(">=", "");
    if (str.contains("<=")) str = str.replace("<=", "");

    return str;
}

QString getOperation(QString str) {
    if (str.contains("=")) return "=";
    if (str.contains(">")) return ">";
    if (str.contains("<")) return "<";
    if (str.contains(">=")) return ">=";
    if (str.contains("<=")) return "<=";

    return "";
}


// 将字符串替换成正则表达式字符串
QString replaceWithRegex(QString str) {
    int index = 0;
    while(str.contains("var")) {
        str.replace("var" + QString::number(++index), "[0-9]+");
    }
    return str;
}

// 根据格式从字符串中匹配出相关的数字，并返回数字的列表
QHash<QString, qint64> getValueListFromStrByFormat(QString str, QString format) {
    QHash<QString, qint64> res;
    QString tFormatStr1 = format;
    QString tFormatStr2 = format;

    int index = 0;
    while(format.contains("var")) {
        format.replace("var" + QString::number(++index), "([0-9]+)");
    }

    QRegularExpression re(format);
    QRegularExpressionMatch match = re.match(str);
    QStringList valueList;
    if (match.hasMatch()) {
        valueList = match.capturedTexts();
    }

    index = 0;
    while(tFormatStr1.contains("var" + QString::number(++index))) {
        tFormatStr1.replace("var" + QString::number(index), "(var" + QString::number(index) + ")");
    }
    re.setPattern(tFormatStr1);
    match = re.match(tFormatStr2);
    QStringList varList;
    if (match.hasMatch()) {
        varList = match.capturedTexts();
    }

    if (valueList.length() != varList.length()) qDebug()<<"error";

    for (int index = 1; index < varList.length(); ++index) {
        res[varList[index]] = valueList[index].toLongLong();
    }

    return res;
}



// 根据运算符号将字符串进行分离
QStringList splitByOperationSign(QString str) {
    QStringList res;
    // 去除空格
    str = str.replace(" ", "");

    if (str.contains(">")) {
        res = str.split(">");
        res.append(">");
    }
    if (str.contains("<")) {
        res = str.split("<");
        res.append("<");
    }
    if (str.contains(">=")) {
        res = str.split(">=");
        res.append(">=");
    }
    if (str.contains("<=")) {
        res = str.split("<=");
        res.append("<=");
    }
    if (str.contains("=")) {
        res = str.split("=");
        res.append("=");
    }

    return res;
}

QList<QStringList> getRules(QString str) {
    QList<QStringList> list;

    QStringList tmpList = str.split(",");

    for (auto const &sub_str : std::as_const(tmpList)) {
        list.append(splitByOperationSign(sub_str));
    }

    return list;
}


QString translationByFormula(QHash<QString, qint64> hash, QString formula) {
    int index = 0;
    while(formula.contains("var")) {
        if (formula.contains("var" + QString::number(++index)))
            formula.replace("var" + QString::number(index), QString::number(hash["var" + QString::number(index)]));
    }
    return formula;
}


QString getStringFromLineByVarName(QString line, QString varName) {
    QString res;
    if (line.contains(varName)) {
        // 根据提供的格式从日志中提取对应的变量字符串
        varName = varName.replace("[", "\\[");
        varName = varName.replace("]", "\\]");
        varName = varName.replace("(", "\\(");
        varName = varName.replace(")", "\\)");

        QRegularExpression re(u8".*[,，\\s]+(" + varName + u8"\\s*=\\s*\\(*(\\d+([,，\\s]*\\d*)*)*\\)*)[,，\\s]+.*");

        QRegularExpressionMatch match = re.match(line);
        if (match.hasMatch()) {
            res = match.captured(1);
        }
    }

    return res;
}

QString getDateTimeFromLog(QString text)
{
    QString res;
    QRegularExpression re(u8"\\b(\\d{2,4}\\d{0,2}[\\\/-]?\\d{1,2}[\\\/-]?\\d{1,2}\\s+\\d{1,2}:\\d{1,2}:\\d{1,2}:\\d{1,3}).*");

    QRegularExpressionMatch match = re.match(text);
    if (match.hasMatch()) {
        res = match.captured(1);
    }

    return  res;
}
